﻿using System;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.Win32;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace OneEightySquaredDbScripter
{
    [XmlRoot]
    public class ScriptingLayout
    {
        #region Fields
        private const string REG_KEY_PATH = "Software\\OneEightySquared\\RionScripter";
        private const string REG_VALUE_NAME = "OutputDirectory";
        #endregion

        #region C'tor
        protected ScriptingLayout()
        {            
            SchemaSubDirectory = "Schema";
            SprocsSubDirectory = "Stored Procedures";
            ViewsSubDirectory = "Views";
            Groups = new List<ScriptingGroup>();
            IsDirty = false;
        }
        #endregion

        #region Properties
        public static string OutputDirectory
        {
            get
            {
                using (RegistryKey key = Registry.LocalMachine.OpenSubKey(REG_KEY_PATH))
                {
                    return (null == key) ? "" : (string)key.GetValue(REG_VALUE_NAME);
                }
            }
            set
            {
                using (RegistryKey key = Registry.LocalMachine.OpenSubKey(REG_KEY_PATH, true))
                {
                    if (null != key)
                    {
                        key.SetValue(REG_VALUE_NAME, value);
                        return;
                    }
                    using (RegistryKey newKey = Registry.LocalMachine.CreateSubKey(REG_KEY_PATH))
                    {
                        newKey.SetValue(REG_VALUE_NAME, value);
                    }
                }
            }
        }


        public string SchemaSubDirectory { get; set; }
        public string ViewsSubDirectory { get; set; }
        public string SprocsSubDirectory { get; set; }

        [XmlArray]
        public List<ScriptingGroup> Groups { get; set; }


        [XmlIgnore]
        public bool IsDirty { get; set; }
        #endregion

        #region Public Methods
        public static ScriptingLayout Create()
        {
            return new ScriptingLayout();
        }

        public static ScriptingLayout Load()
        {
            string pathName = ScriptingLayout.OutputDirectory;
            if (0 == pathName.Length)
            {
                throw new InvalidOperationException("OutputDirectory has not been set.\nThis should be the root of your Projects.");
            }

            string fileName = Path.Combine(pathName, "ScriptingLayout.xml");
            XmlSerializer s = new XmlSerializer(typeof(ScriptingLayout));
        
            TextReader r = new StreamReader(fileName);
            ScriptingLayout layout = (ScriptingLayout)s.Deserialize(r);
            r.Close();      

            return layout;
        }

        public static void Save(ScriptingLayout layout)
        {
            string pathName = ScriptingLayout.OutputDirectory;
            if (0 == pathName.Length)
            {
                throw new InvalidOperationException("OutputDirectory has not been set.\nThis should be the root of your Workspace.");
            }

            string fileName = Path.Combine(pathName, "ScriptingLayout.xml");
            XmlSerializer s = new XmlSerializer(typeof(ScriptingLayout));            
            TextWriter w = new StreamWriter(fileName);
            s.Serialize(w, layout);
            w.Close();

            layout.IsDirty = false;
        }

        public bool GroupExists(string groupName)
        {
            foreach (ScriptingGroup group in Groups)
            {
                if (group.GroupName.ToUpperInvariant() == groupName.ToUpperInvariant())
                {
                    return true;
                }
            }
            return false;
        }

        public void AddNewGroup(string groupName)
        {
            ScriptingGroup newGroup = new ScriptingGroup(groupName);
            Groups.Add(newGroup);
            IsDirty = true;
        }

        public void RenameGroup(string oldName, string newName)
        { 
            foreach (ScriptingGroup group in Groups)
            {
                if (group.GroupName.ToUpperInvariant() == oldName.ToUpperInvariant())
                {
                    group.GroupName = newName;
                    break;
                }
            }
            IsDirty = true;
        }

        public void DeleteGroup(string groupName)
        {
            foreach (ScriptingGroup group in Groups)
            {
                if (group.GroupName.ToUpperInvariant() == groupName.ToUpperInvariant())
                {
                    Groups.Remove(group);
                    break;
                }
            }
            IsDirty = true;
        }

        public bool SprocExistsInGroup(string name)
        {
            foreach (ScriptingGroup group in Groups)
            {
                if (group.Sprocs.Contains(name)) return true;
            }
            return false;
        }

        public bool TableExistsInGroup(string name)
        {
            foreach (ScriptingGroup group in Groups)
            {
                if (group.Schema.Contains(name)) return true;
            }
            return false;
        }

        public bool ViewExistsInGroup(string name)
        {
            foreach (ScriptingGroup group in Groups)
            {
                if (group.Views.Contains(name)) return true;
            }
            return false;
        }

        public delegate bool MoveDelegate(string name, string currentGroup, bool moveUp);

        public bool MoveTableToNewGroup(string name, string currentGroup, bool moveUp)
        {
            int currentIndex = GetGroupIndex(currentGroup);
            int newIndex = GetNewGroupIndex(currentIndex, moveUp);
            if (-2 == newIndex) return false;

            if (currentIndex >= 0)
            {
                ScriptingGroup oldGroup = Groups[currentIndex];
                oldGroup.Schema.Remove(name);
            }
            if (newIndex >= 0)
            {
                ScriptingGroup newGroup = Groups[newIndex];
                newGroup.Schema.Add(name);
            }
            IsDirty = true;
            return true;
        }

        public bool MoveSprocToNewGroup(string name, string currentGroup, bool moveUp)
        {
            int currentIndex = GetGroupIndex(currentGroup);
            int newIndex = GetNewGroupIndex(currentIndex, moveUp);
            if (-2 == newIndex) return false;

            if (currentIndex >= 0)
            {
                ScriptingGroup oldGroup = Groups[currentIndex];
                oldGroup.Sprocs.Remove(name);
            }
            if (newIndex >= 0)
            {
                ScriptingGroup newGroup = Groups[newIndex];
                newGroup.Sprocs.Add(name);
            }
            IsDirty = true;
            return true;
        }

        public bool MoveViewToNewGroup(string name, string currentGroup, bool moveUp)
        {
            int currentIndex = GetGroupIndex(currentGroup);
            int newIndex = GetNewGroupIndex(currentIndex, moveUp);
            if (-2 == newIndex) return false;

            if (currentIndex >= 0)
            {
                ScriptingGroup oldGroup = Groups[currentIndex];
                oldGroup.Views.Remove(name);
            }
            if (newIndex >= 0)
            {
                ScriptingGroup newGroup = Groups[newIndex];
                newGroup.Views.Add(name);
            }
            IsDirty = true;
            return true;
        }

        public bool MoveTableToNewGroup(string name, string currentGroupName, string newGroupName)
        {
            int indexCurrentGroup = GetGroupIndex(currentGroupName);
            int indexNewGroup = GetGroupIndex(newGroupName);

            if (indexCurrentGroup >= 0)
            {
                ScriptingGroup oldGroup = Groups[indexCurrentGroup];
                oldGroup.Schema.Remove(name);
            }
            if (indexNewGroup >= 0)
            {
                ScriptingGroup newGroup = Groups[indexNewGroup];
                newGroup.Schema.Add(name);
            }

            IsDirty = true;
            return true;
        }

        public bool MoveViewToNewGroup(string name, string currentGroupName, string newGroupName)
        {
            int indexCurrentGroup = GetGroupIndex(currentGroupName);
            int indexNewGroup = GetGroupIndex(newGroupName);

            if (indexCurrentGroup >= 0)
            {
                ScriptingGroup oldGroup = Groups[indexCurrentGroup];
                oldGroup.Views.Remove(name);
            }
            if (indexNewGroup >= 0)
            {
                ScriptingGroup newGroup = Groups[indexNewGroup];
                newGroup.Views.Add(name);
            }

            IsDirty = true;
            return true;
        }

        public bool MoveSprocToNewGroup(string name, string currentGroupName, string newGroupName)
        {
            int indexCurrentGroup = GetGroupIndex(currentGroupName);
            int indexNewGroup = GetGroupIndex(newGroupName);

            if (indexCurrentGroup >= 0)
            {
                ScriptingGroup oldGroup = Groups[indexCurrentGroup];
                oldGroup.Sprocs.Remove(name);
            }
            if (indexNewGroup >= 0)
            {
                ScriptingGroup newGroup = Groups[indexNewGroup];
                newGroup.Sprocs.Add(name);
            }

            IsDirty = true;
            return true;
        }

        public string GetSprocPathname(string name)
        {
            foreach (ScriptingGroup group in Groups)
            {
                if (group.Sprocs.Contains(name))
                {
                    string pathName = Path.Combine(OutputDirectory, group.GroupName);
                    pathName = Path.Combine(pathName, SprocsSubDirectory);
                    return Path.Combine(pathName, name + ".sql");
                }
            }
            string fileName = Path.Combine(OutputDirectory, SprocsSubDirectory);
            return Path.Combine(fileName, name + ".sql");
        }

        public string GetTablePathname(string rootDirectory, string name)
        {
            foreach (ScriptingGroup group in Groups)
            {
                if (group.Schema.Contains(name))
                {
                    string pathName = Path.Combine(rootDirectory, group.GroupName);
                    pathName = Path.Combine(pathName, SchemaSubDirectory);
                    return Path.Combine(pathName, name + ".sql");
                }
            }
            string fileName = Path.Combine(rootDirectory, SchemaSubDirectory);
            return Path.Combine(fileName, name + ".sql");
        }

        public string GetViewPathname(string name)
        {
            foreach (ScriptingGroup group in Groups)
            {
                if (group.Views.Contains(name))
                {
                    string pathName = Path.Combine(OutputDirectory, group.GroupName);
                    pathName = Path.Combine(pathName, ViewsSubDirectory);
                    return Path.Combine(pathName, name + ".sql");
                }
            }
            string fileName = Path.Combine(OutputDirectory, ViewsSubDirectory);
            return Path.Combine(fileName, name + ".sql");
        }
        #endregion

        #region Private Methods
        private int GetGroupIndex(string groupName)
        {
            for (int currentIndex = 0; currentIndex < Groups.Count; currentIndex++)
            {
                ScriptingGroup group = Groups[currentIndex];
                if (group.GroupName.ToUpperInvariant() == groupName.ToUpperInvariant()) return currentIndex;
            }
            return -1;
        }

        private int GetNewGroupIndex(int currentIndex, bool moveUp)
        {
            if (moveUp)
            {
                if (-1 == currentIndex) return -2;
                return currentIndex - 1;
            }
            else
            {
                if (Groups.Count == currentIndex + 1) return -2;
                return currentIndex + 1;
            }
        }
        #endregion
    }
}
